//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// Copyright 2012 Microsoft Corporation. All Rights Reserved
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.   
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

namespace Preflight.ServiceModel
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web.Http;
    using System.Web.Routing;
    using Model;

    public static class PreflightService
    {
        private const string HttpRouteName = "Preflight-EVT-API";
        private const string RouteTemplate = "preflight-api/{action}";
        private const string ControllerName = "preflight";

        public static void Initialize(PreflightContext preflightContext, PreflightServiceOptions serviceOptions)
        {
            Log.LogVerbose("Initializing engine");
            Engine.Initialize(preflightContext);

            Log.LogVerbose("Initializing Service");
            InitializeService(serviceOptions);

            Log.LogVerbose("Initialization complete");
        }

        public static void InitializeFromConfiguration()
        {
            Log.LogVerbose("Initializing engine from configuration");
            Engine.InitializeFromConfiguration();

            Log.LogVerbose("Initializing service from configuration");
            var serviceOptions = new ServiceConfigurationReader().Read();

            InitializeService(serviceOptions);
            Log.LogVerbose("Initialization complete");
        }

        internal static void MapHttpRoute(HttpRouteCollection routes)
        {
            Log.LogVerbose("Adding HttpRoute: Name: {0} RouteTemplate: {1} ControllerName:", HttpRouteName, RouteTemplate, ControllerName);
            routes.MapHttpRoute(
                HttpRouteName,
                RouteTemplate,
                new { controller = ControllerName });
        }

        private static void InitializeService(PreflightServiceOptions serviceOptions)
        {
            VerifyOptions(serviceOptions);

            Log.LogVerbose("Initializing service with the following configuration: {0}", serviceOptions);
            RuntimeValueStore.SetValue(StateKeys.ServiceOptions, serviceOptions);

            Log.LogVerbose("Adding HttpRoute: Name: {0} RouteTemplate: {1} ControllerName:", HttpRouteName, RouteTemplate, ControllerName);
            try
            {
                RouteTable.Routes.MapHttpRoute(HttpRouteName, RouteTemplate, new { controller = ControllerName });
            }
            catch (ArgumentException exception)
            {
                if (exception.ParamName != "name")
                {
                    throw;
                }
            }
        }

        private static void VerifyOptions(PreflightServiceOptions serviceOptions)
        {
            var validators = new List<Action<PreflightServiceOptions>>
                               {
                                   ValidatePreflightSecurityOptions,
                                   ValidateWindowsAuthenticationOptions,
                                   ValidateAcsOptions
                               };

            validators.ForEach(validator => validator(serviceOptions));
        }

        private static void ValidatePreflightSecurityOptions(PreflightServiceOptions serviceOptions)
        {
            if (serviceOptions.SecurityMode != PreflightServiceSecurityMode.PreflightSecurity)
            {
                return;
            }

            if (serviceOptions.CertificateLocation == null && serviceOptions.CustomAuthorization == null)
            {
                Fail("Security is enabled but no certificate or custom auth object defined");
            }
        }

        private static void ValidateWindowsAuthenticationOptions(PreflightServiceOptions serviceOptions)
        {
            if (serviceOptions.SecurityMode != PreflightServiceSecurityMode.WindowsAuthentication)
            {
                return;    
            }

            if (serviceOptions.Roles == null)
            {
                Fail("Windows Authentication is enabled but Roles property is null");
            }

            if (serviceOptions.Roles.Any() == false)
            {
                Fail("Windows Authentication is enabled but Role property is empty");
            }
        }

        private static void ValidateAcsOptions(PreflightServiceOptions serviceOptions)
        {
            if (serviceOptions.SecurityMode != PreflightServiceSecurityMode.ACSSimpleWebToken)
            {
                return;
            }

            if (serviceOptions.AcsOptions == null)
            {
                Fail("ACS SWT security option is enabled but acs settings have not been set");
            }

            if (string.IsNullOrWhiteSpace(serviceOptions.AcsOptions.Issuer))
            {
                Fail("ACS issuer not provided");
            }

            if (string.IsNullOrWhiteSpace(serviceOptions.AcsOptions.Audience))
            {
                Fail("ACS audience not provided");
            }

            if (string.IsNullOrWhiteSpace(serviceOptions.AcsOptions.SigningKey))
            {
                Fail("ACS signing key not provided");
            }
        }

        private static void Fail(string errorMessage)
        {
            Log.LogError(errorMessage);
            throw new InvalidOperationException(errorMessage);
        }
    }
}
